<template>
  <view class="tabs flex-col" :class="customClass">
    <view
      class="tabs-sticky"
      :style="{
        top: $u.addUnit(stickyTop, 'rpx'),
      }">
      <tabs-el
        :list="tabs"
        :current="tabIndex"
        keyName="label"
        lineWidth="60rpx"
        lineHeight="8rpx"
        :activeStyle="{ color: '#4374FF' }"
        :inactiveStyle="{ color: '#666' }"
        :evenly="evenly"
        :border="padding"
        :scrollable="scrollable"
        :bgColor="tabsBgColor"
        :autoWidth="tabsLineAutoWidth"
        @change="handleTabsChange" />
    </view>
    <swiper
      :style="[{ height: swiperHeight }, $u.addStyle(swiperStyle)]"
      class="tabs-swiper"
      :class="{ padding }"
      :current="tabIndex"
      :duration="duration"
      @transition="swiperTransition"
      @animationfinish="swiperAnimationFinish">
      <template v-if="touchMove">
        <swiper-item
          v-for="item in tabs"
          :key="item.name"
          class="swiper-item overflow-auto"
          :class="[item.sClass, { 'h-fit': autoHeight }]">
          <scroll-view
            enable-flex
            class="tabs-swiper-scroll"
            scroll-y
            enable-passive
            enhanced
            :scroll-top="scroll[item.name]"
            @scroll="e => handleScroll(e, item.name)">
            <slot v-if="item.name !== 'null'" :name="item.name"></slot>
          </scroll-view>
        </swiper-item>
      </template>
      <template v-else>
        <swiper-item
          v-for="item in tabs"
          :key="item.name"
          class="swiper-item overflow-auto"
          :class="[item.sClass, { 'h-fit': autoHeight }]"
          @touchmove.stop>
          <scroll-view
            enable-flex
            class="tabs-swiper-scroll"
            scroll-y
            enable-passive
            enhanced
            :scroll-top="scroll[item.name]"
            @scroll="e => handleScroll(e, item.name)">
            <slot v-if="item.name !== 'null'" :name="item.name"></slot>
          </scroll-view>
        </swiper-item>
      </template>
    </swiper>
  </view>
</template>

<script>
const DEFAULT_DURATION = 300;
export default {
  name: "Tabs",
  // #ifdef MP-WEIXIN
  options: {
    //开启虚拟节点,去除外面多包裹的标签
    //(导致与uview一样不能接收class和style,只能通过customXXX传入)
    virtualHost: true,
  },
  // #endif
  props: {
    value: {
      type: String,
      default: "",
    },
    touchMove: {
      // 是否开启手动滑动
      type: Boolean,
      default: true,
    },
    scrollable: {
      // tabs是否可滚动
      type: Boolean,
      default: false,
    },
    autoHeight: {
      // 是否自动设置高度
      type: Boolean,
      default: false,
    },
    tabsLineAutoWidth: {
      //line自动宽度
      type: Boolean,
      default: false,
    },
    tabsBgColor:  {
      type: String,
      default: "#fff",
    },
    stickyTop: {
      type: [String, Number],
      default: 0,
    }, //锚点导航距离顶部距离
    swiperStyle: {
      type: [String, Object],
      default: () => ({}),
    },
    initAutoHeight: {
      // 是否初始化自动设置高度
      type: Boolean,
      default: true,
    },
    padding: {
      //给swiper-item增加内边距
      type: Boolean,
      default: false,
    },
    evenly: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      tabs: [],
      duration: DEFAULT_DURATION,
      tabIndex: 0,
      visibleList: [],
      scroll: {},
      swiperHeight: "100%",
    };
  },
  mounted() {
    this.initTabs();
    if (this.value) this.updateCurrentName();
    else this.tabIndexChange(this.tabIndex);
    if (this.autoHeight && this.initAutoHeight)
      setTimeout(this.updateSwiperHeight);
  },
  methods: {
    swiperTransition({ detail }) {
      if (this.moveLock) return;
      this.moveLock = true;
      let index = this.tabIndex;
      if (this.notTouchMove) {
        this.notTouchMove = false;
      } else {
        index = this.tabIndex + Math.sign(detail.dx);
      }
      this.updateSwiperHeight(index);
    },
    swiperAnimationFinish({ detail }) {
      if (this.moveLock) {
        this.moveLock = false;
        this.tabIndex = detail.current;
        this.updateSwiperHeight(this.tabIndex);
      }
    },
    visible(name) {
      return this.visibleList.includes(name);
    },
    emitInput({ name } = {}) {
      this.$emit("input", name);
      if (!name || this.visibleList.includes(name)) return;
      this.visibleList.push(name);
    },
    initTabs() {
      const tabs = [];
      const children = this.$children;
      for (const index in children) {
        const item = children[index];
        if (item.$options.name === "TabsPane") {
          let { name, label, sClass } = item;
          name = name ?? index;
          label = label ?? name;
          tabs.push({ label, name, sClass });
        }
      } //读取TabsPane数据
      this.tabs = tabs;
    },
    handleScroll(e, name) {
      //存储滚动位置
      uni.$u.debounce(() => {
        this.scroll[name] = e.detail.scrollTop;
      }, 60);
    },
    handleTabsChange(item) {
      //u-tabs切换
      this.notTouchMove = true;
      this.tabIndex = item.index;
      this.$emit("change", item);
    },
    updateCurrentName(value = this.value) {
      //更新当前选中的name
      const index = this.tabs.findIndex(o => value === o.name);
      if (index !== -1) this.tabIndex = index;
    },
    async updateSwiperHeight(index) {
      //更新swiper高度
      if (!index) index = this.tabIndex;
      const items = await this.$uGetRect(".swiper-item", true);
      const { height = null } = items[index] || {};
      if (height !== null) {
        const fn = () => (this.swiperHeight = height + "px");
        clearTimeout(this.timer);
        if (parseInt(this.swiperHeight) <= height) return fn();
        if (!this.moveLock) {
          this.timer = setTimeout(fn, DEFAULT_DURATION);
        }
      }
    },
    tabIndexChange(index) {
      this.emitInput(this.tabs[index]);
    },
  },
  watch: {
    value(val) {
      this.updateCurrentName(val);
    },
    tabIndex(index) {
      this.tabIndexChange(index);
    },
  },
};
</script>

<style lang="scss">
.tabs {
  &-sticky {
    position: sticky;
    z-index: 2023;
  }
  height: 100%;
  .tabs-swiper {
    flex-grow: 1;
    height: fit-content;
    margin-top: 12rpx;
    &-scroll {
      height: 100%;
    }
    .h-fit {
      height: fit-content !important;
    }
    &.padding .swiper-item {
      padding: 0 32rpx 20rpx;
      box-sizing: border-box;
    }
  }
}
</style>
